package com.lemon.cloud.mp.web;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.lemon.cloud.comm.constants.CacheConstants;
import com.lemon.cloud.comm.exception.BusinessException;
import com.lemon.cloud.comm.model.ResultMsg;
import com.lemon.cloud.comm.po.SocialParams;
import com.lemon.cloud.comm.po.SocialUser;
import com.lemon.cloud.mp.config.WxMpInitConfigService;
import com.lemon.cloud.mp.entity.WxAccount;
import com.lemon.cloud.mp.entity.WxAccountFans;
import com.lemon.cloud.mp.service.WxAccountFansService;
import com.lemon.cloud.mp.service.WxAccountService;
import com.lemon.cloud.security.annotation.Inner;
import com.lemon.cloud.security.model.SecurityUser;
import com.lemon.cloud.security.util.SecurityUtils;
import io.swagger.v3.oas.annotations.Hidden;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.service.WxOAuth2Service;
import me.chanjar.weixin.mp.api.WxMpService;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.lemon.cloud.comm.constants.CommonConstants.ONE;

/**
 * 微信授权服务
 */
@RestController
@Slf4j
@RequestMapping("/auth")
public class WxAuthController {

    @Autowired
    private WxAccountFansService accountFansService;

    @Autowired
    private WxAccountService accountService;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 网页授权
     */
    @PostMapping("/index")
    @Inner(false)
    public ResultMsg<String> index(@RequestBody SocialParams socialParams){
        String appId = socialParams.getAppId();
        Assert.state(StringUtils.isNotEmpty(appId), "微信公众号授权appId不能为空");
        WxAccount wxAccount = accountService
                .getOne(Wrappers.<WxAccount>query().lambda().eq(WxAccount::getAppid, appId));
        Assert.state(ObjectUtil.isNotNull(wxAccount),"微信公众号授权不存在");
        String redirectUrl = wxAccount.getFrontUrl();
        Assert.state(StringUtils.isNotEmpty(redirectUrl), "微信公众号授权redirectUrl不能为空");
        redirectUrl = buildParams(redirectUrl, socialParams);
        String state = RandomUtil.randomString(4);;
        redisTemplate.opsForValue().set(CacheConstants.MP_PARAMS_STATE + state, StrUtil.EMPTY, ONE, TimeUnit.MINUTES);
        final WxMpService wxMpService = WxMpInitConfigService.getMpServices().get(appId);
        if (wxMpService == null) {
            log.error("wxMpService buildAuthorizationUrl");
            throw new BusinessException("微信公众号授权异常");
        }
        WxOAuth2Service oAuth2Service = wxMpService.getOAuth2Service();
        return ResultMsg.resultSuccess(oAuth2Service.buildAuthorizationUrl(redirectUrl, WxConsts.OAuth2Scope.SNSAPI_USERINFO, state));
    }

    private String buildParams(String redirectUrl, SocialParams socialParams) {
        try {
            String route = Optional.ofNullable(socialParams.getRedirectUrl()).orElse(StrUtil.EMPTY);
            String paramsStr = Optional.ofNullable(socialParams.getParams())
                    .map(params -> {
                        try {
                            // 将 Map 转换为 URL 编码的查询字符串
                            return params.entrySet().stream()
                                    .map(entry -> URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8) +
                                            "=" + URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8))
                                    .collect(Collectors.joining("&"));
                        } catch (Exception e) {
                            return StrUtil.EMPTY;
                        }
                    })
                    .orElse(StrUtil.EMPTY);

            return redirectUrl +
                    "?route=" + URLEncoder.encode(route, StandardCharsets.UTF_8) +
                    "&params=" + paramsStr;
        } catch (Exception e) {
            // 异常处理，根据实际需求决定是抛出还是返回默认值
            return redirectUrl;
        }
    }

    /**
     * 网页授权回调
     * WxAccount callbackUrl配置的此接口地址
     *
     * @param params
     */
    @PostMapping("/getSocialUser")
    @Inner
    @Hidden
    public ResultMsg<SocialUser> getSocialUser(@RequestBody SocialParams params) {
        log.info("come in getSocialUser");
        String appId = params.getAppId();
        SocialUser user = new SocialUser();
        try {
            final WxMpService wxMpService = WxMpInitConfigService.getMpServices().get(appId);
            final WxOAuth2Service oAuth2Service = wxMpService.getOAuth2Service();
            WxOAuth2AccessToken accessToken = oAuth2Service.getAccessToken(params.getCode());
            WxOAuth2UserInfo wxOAuth2UserInfo = oAuth2Service.getUserInfo(accessToken, "zh_CN");
            WxAccountFans wxAccountFans = accountFansService.saveOrUpdateByOauth2(appId, wxOAuth2UserInfo);
            user.setUserId(wxAccountFans.getId())
                    .setProviderId(params.getAppId())
                    .setProviderUserId(wxOAuth2UserInfo.getOpenid())
                    .setGender(wxOAuth2UserInfo.getSex())
                    .setDisplayName(wxOAuth2UserInfo.getNickname())
                    .setImageUrl(wxOAuth2UserInfo.getHeadImgUrl())
                    .setAccessToken(accessToken.getAccessToken())
                    .setRefreshToken(accessToken.getRefreshToken())
                    .setExpireTime((long) accessToken.getExpiresIn())
                    .setState(params.getState());
        } catch (WxErrorException e) {
            log.error("微信授权异常：{}", e.getLocalizedMessage());
            log.error("微信授权异常,{}", e.getError());
            throw new BusinessException("微信授权异常");
        }
        return ResultMsg.resultSuccess(user);
    }

    /**
     * openId 获取用户
     * @param providerUserId
     * @return
     */
    @PostMapping("/getSocialUserByOpenId")
    @Inner
    @Hidden
    public ResultMsg<SocialUser> getSocialUserByOpenId(@RequestParam String providerUserId) {
        SocialUser user = new SocialUser();
        WxAccountFans wxOAuth2UserInfo = accountFansService.getOne(new LambdaQueryWrapper<WxAccountFans>().eq(WxAccountFans::getOpenid, providerUserId).last("limit 1"));
        user.setUserId(wxOAuth2UserInfo.getId())
                .setProviderId(wxOAuth2UserInfo.getWxAccountAppid())
                .setProviderUserId(wxOAuth2UserInfo.getOpenid())
                .setGender(Integer.valueOf(wxOAuth2UserInfo.getGender()))
                .setDisplayName(wxOAuth2UserInfo.getNickname())
                .setImageUrl(wxOAuth2UserInfo.getHeadimgUrl());

        return ResultMsg.resultSuccess(user);
    }

    private String buildParams(String url, Map<String, String> params) {
        if (CollectionUtil.isNotEmpty(params)) {
            UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
            for (Map.Entry<String, String> entry : params.entrySet()) {
                builder.queryParam(entry.getKey(), entry.getValue());
            }
            url = builder.toUriString();
        }
        return url;
    }

    /**
     * 获取当前用户信息
     * @return
     */
    @GetMapping("/info")
    public ResultMsg<SocialUser> getInfo(){
        SecurityUser user = SecurityUtils.getUser();
        return ResultMsg.resultSuccess(user.getSocialUser());
    }

}
